package com.socio.activities;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;
import oauth.signpost.commonshttp.CommonsHttpOAuthProvider;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.telephony.TelephonyManager;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.facebook.android.DialogError;
import com.facebook.android.Facebook;
import com.facebook.android.FacebookError;
import com.facebook.android.Facebook.DialogListener;
import com.socio.model.ClientUser;
import com.socio.threads.BackendThread;

public class SocioSettings extends Activity {

	public static final String FOURSQUARE_CALLBACK_URL = "socio://foursquare";
	public static final String FOURSQUARE_CLIENT_ID = "IADFYQURQ3CEZ52MQAG45WDHRTLB24NBZ0IF2X1DCNKTT1MD";
	public static final String FOURSQUARE_CLIENT_SECRET = "0A3HG3CEIAV4QXNLTONPIJT2R3R4LRMZGULCONOOXE5NLOOI";
	
	// different types of dialogs
	private static final int SETTINGS_PROGRESS_DIALOG = 0;
	private static final int NO_NICKNAME_DIALOG = 2;
	private static final int NICKNAME_TOO_LONG = 3;
	private static final int CANCEL_FACEBOOK_DIALOG = 6;
	private static final int AUTHORIZATION_PROGRESS_DIALOG = 7;
	private static final String TWITTER_CALLBACK_URL = "socio://twitt";
	private static final String TWITTER_CONSUMER_KEY = "CEGOnFaZ2wj6b7dlfDIfg";
	private static final String TWITTER_CONSUMER_SECRET = "jlJVVIGwzjGdxjnX9TJAnsBeMsfOggoyZpPUsOMrU";
	private static final String FOURSQUARE_AUTHORIZATION_URL = "https://foursquare.com/oauth2/authenticate?client_id=" +FOURSQUARE_CLIENT_ID+ "&response_type=code&display=touch&redirect_uri=" +FOURSQUARE_CALLBACK_URL;
	private static final String FACEBOOK_APP_ID = "135216369868379";
	private static final Map<String, Object> paramsForMessage = new HashMap<String, Object>();
	private static final DialogInterface.OnClickListener onClickDismiss = new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); }};
	private static final List<ProgressDialog> createdProgressDialogs = new ArrayList<ProgressDialog>();
	private static final CommonsHttpOAuthProvider twitterProvider = new CommonsHttpOAuthProvider("https://api.twitter.com/oauth/request_token", "https://api.twitter.com/oauth/access_token",  "https://api.twitter.com/oauth/authorize");
	
	public static final CommonsHttpOAuthConsumer twitterConsumer = new CommonsHttpOAuthConsumer(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET);
	
	private static InputMethodManager softKeyboard;
	private static TelephonyManager telephonyManager;
	private static ClientUser displayedUser;
	
	// Define the Handler that receives messages from the search thread and update the progress
    private final Handler handler = new Handler() { public void handleMessage(Message msg) { handleMessages(msg);}};
	
	// view components
	private EditText nickname;
	private CheckBox facebookCheckbox;
	private CheckBox twitterCheckbox;
	private CheckBox foursquareCheckbox;
	private Button saveButton;
	private Handler backendThreadsHandler = new Handler() { public void handleMessage(Message msg) { fakeMessageHandles(msg);}};
	private Message messageToBeResumed;
	private BackendThread backendThread;
	
	/**
	 * Authorizes Socio to post to users wall
	 */
	private void authorizeThroughFacebook() {
		final Facebook facebook = new Facebook(FACEBOOK_APP_ID);
		facebook.authorize(this, 
                new String[] {},
                new DialogListener(){
					public void onCancel() {}
					public void onComplete(Bundle values) {
						submitFacebookID(facebook.getAccessToken());
					}
					public void onError(DialogError e) {}
					public void onFacebookError(FacebookError e) {}
			
				});
	}
	
	/**
	 * Authorizes Hooopla to read users checkins
	 */
	private void authorizeThroughFoursquare() {
		try {
			startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(FOURSQUARE_AUTHORIZATION_URL)));
		} catch (Exception e) {}  
		
	}
	
	/**
	 * Authorizes Hooopla to read users timeline
	 */
	private void authorizeThroughTwitter() {
		try {
			String authUrl = twitterProvider.retrieveRequestToken(twitterConsumer, TWITTER_CALLBACK_URL);
			startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl)));
		} catch (Exception e) {}  
		
	}
	
	/**
     * Dismiss all progress dialogs currently shown.
     */
    private void dismissAndRemoveProgressDialogs() {
    	Iterator<ProgressDialog> progressDialogIterator = createdProgressDialogs.iterator();
    	while(progressDialogIterator.hasNext()) {
    		ProgressDialog createdProgressDialog = progressDialogIterator.next();
    		createdProgressDialog.dismiss();
    		progressDialogIterator.remove();
    	}
    }
    
    /**
	 * Displays a user's settings
	 * 
	 * @param user user with settings to display
	 */
	private void displaySettings(ClientUser user) {
		displayedUser = user;
		
		// set nickname
		String userNickname = user.getNickname();
		if(userNickname.length() != 0)
			nickname.setText(userNickname, TextView.BufferType.EDITABLE);
		
		// handle network enables
		if(user.getFacebookID().length() > 0)
			facebookCheckbox.setChecked(true);
		else
			facebookCheckbox.setChecked(false);
		
		// since twitter access token was added later it can be null
		String twitterAccessToken = user.getTwitterAccessToken();
		if(twitterAccessToken != null && twitterAccessToken.length() > 0)
			twitterCheckbox.setChecked(true);
		else
			twitterCheckbox.setChecked(false);
		
		dismissAndRemoveProgressDialogs();
	}
    
    /**
	 * Temporary handler in place of backend thread handle that will just have messages resent
	 * until the backend thread is initialized and gives us its handler
	 * 
	 * @param msg Message to resend
	 */
    private void fakeMessageHandles(Message msg) {
    	msg.arg2 = msg.arg1 = BackendThread.TRY_AGAIN;
    	handleMessages(msg);
    }
    
    /**
	 * Gets a users' settings from web
	 */
	private void getSettings() {
		showDialog(SETTINGS_PROGRESS_DIALOG);
		
		paramsForMessage.put("udid", telephonyManager.getDeviceId());
		
		// send message when current location is received
		final Message message = backendThreadsHandler.obtainMessage(BackendThread.GET_SETTINGS);
		message.obj = paramsForMessage;
		message.sendToTarget();
	}
    
    /**
	 * Handles get settings messages
	 * 
	 * @param msg Get settings message
	 */
	private void handleGetSettingsMessage(Message msg) {
		switch(msg.arg1) {
		case BackendThread.SUCCESS:
			displaySettings((ClientUser)msg.obj);
			break;
		case BackendThread.TROUBLE:
			displaySettings(new ClientUser());
			Toast.makeText(getApplicationContext(), "Trouble getting your settings. Please try again later.", Toast.LENGTH_LONG).show();
			break;
		case BackendThread.TRY_AGAIN:
			Message tryAgainMessage = new Message();
			tryAgainMessage.copyFrom(msg);
			tryAgainMessage.setTarget(backendThreadsHandler);
			tryAgainMessage.sendToTarget();
			break;
		}
	}
    
    /**
	 * Handle message return from threads
	 * 
	 * @param msg Message returned from thread
	 */
	private void handleMessages(Message msg) {
		switch(msg.what) {
		case BackendThread.GET_SETTINGS:
			handleGetSettingsMessage(msg);
			break;
		case BackendThread.RECEIVE_HANDLER:
			backendThreadsHandler = (Handler)msg.obj;
			break;
		case BackendThread.UPDATE_FACEBOOKID:
		case BackendThread.UPDATE_TWITTERACCESSTOKEN:
			handleUpdateUserMessage(msg);
			break;
		case BackendThread.UPDATE_SETTINGS:
			handleUpdateSettingsMessage(msg);
			break;
		}
	}
	
	/**
	 * Handles update settings messages
	 * 
	 * @param msg Update settings message
	 */
	private void handleUpdateSettingsMessage(Message msg) {
		switch(msg.arg1) {
		case BackendThread.SUCCESS:
			dismissAndRemoveProgressDialogs();
			Toast.makeText(getApplicationContext(), "Settings updated.", Toast.LENGTH_LONG).show();
			break;
		case BackendThread.TROUBLE:
			dismissAndRemoveProgressDialogs();
			Toast.makeText(getApplicationContext(), "Trouble getting your settings. Please try again later.", Toast.LENGTH_LONG).show();
			break;
		case BackendThread.TRY_AGAIN:
			Message tryAgainMessage = new Message();
			tryAgainMessage.copyFrom(msg);
			tryAgainMessage.setTarget(backendThreadsHandler);
			tryAgainMessage.sendToTarget();
			break;
		}
	}
	
	/**
	 * Handles update user messages
	 * 
	 * @param msg Update user message
	 */
	private void handleUpdateUserMessage(Message msg) {
		switch(msg.arg1) {
		case BackendThread.SUCCESS:
			dismissAndRemoveProgressDialogs();
			facebookCheckbox.setChecked(true);
			Toast.makeText(getApplicationContext(), "Socio authorization modified.", Toast.LENGTH_LONG).show();
			break;
		case BackendThread.TROUBLE:
			dismissAndRemoveProgressDialogs();
			Toast.makeText(getApplicationContext(), "Trouble authorizing Socio. Please try again later.", Toast.LENGTH_LONG).show();
			break;
		case BackendThread.TRY_AGAIN:
			Message tryAgainMessage = new Message();
			tryAgainMessage.copyFrom(msg);
			tryAgainMessage.setTarget(backendThreadsHandler);
			tryAgainMessage.sendToTarget();
			break;
		}
	}
	
	/**
	 * Validate user's proposed nickname
	 * 
	 * @return if nickname is valid
	 */
	private boolean isValidNickname() {
		if(nickname.getText().toString().length() < 1) {
			showDialog(NO_NICKNAME_DIALOG);
			return false;
		}
		
		if(nickname.getText().toString().length() > 8) {
			showDialog(NICKNAME_TOO_LONG);
			return false;
		}
		
		return true;
	}
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
	    setContentView(R.layout.settings);
	    
	    // start backend thread loop ASAP
        backendThread = new BackendThread(getApplicationContext(), handler);
        backendThread.start();
	    
	    nickname = (EditText)findViewById(R.id.Nickname);
	    facebookCheckbox = (CheckBox)findViewById(R.id.FacebookNetworkCheckBox);
	    twitterCheckbox = (CheckBox)findViewById(R.id.TwitterNetworkCheckBox);
	    foursquareCheckbox = (CheckBox)findViewById(R.id.FoursquareNetworkCheckBox);
	    saveButton = (Button)findViewById(R.id.SaveButton);
	    
	    // get system services
	    if(softKeyboard == null)
	    	softKeyboard = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
	    if(telephonyManager == null)
	    	telephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
	    
	    // input key listener
	    nickname.setOnKeyListener(new OnKeyListener() {
			public boolean onKey(View v, int keyCode, KeyEvent event) {
				if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
					softKeyboard.hideSoftInputFromWindow(v.getWindowToken(), 0);
					saveButton.requestFocus();
					return true;
				}
				return false;
			}
		});
	    
	    // save and submit nickname when save button is clicked
	    saveButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { submitNickname(); }});
	    // send a user to authorize Socio is checkbox is clicked
	    facebookCheckbox.setOnClickListener(new OnClickListener() { public void onClick(View v) { toggleFacebook(); }});
	    twitterCheckbox.setOnClickListener(new OnClickListener() { public void onClick(View v) { toggleTwitter(); }});
	    foursquareCheckbox.setOnClickListener(new OnClickListener() { public void onClick(View v) { toggleFoursquare(); }});
	    
	    // re-create saved state
	    if(savedInstanceState != null) {
	    	displaySettings(displayedUser);
	    } else {
	    	getSettings();
	    }
	}
	
	/**
	 * Creates dialogs
	 */
	@Override
	protected Dialog onCreateDialog(int id) {
		Dialog dialog = null;
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		ProgressDialog progressDialog = new ProgressDialog(this);
		progressDialog.setCancelable(true);
		switch (id) {
		case AUTHORIZATION_PROGRESS_DIALOG:
			progressDialog.setMessage("Modifying Socio Authorization. Please wait...");
			dialog = progressDialog;
			break;
		case CANCEL_FACEBOOK_DIALOG:
			builder.setMessage("If you no longer want to receive points for your Facebook updates," +
					" you must go to Facebook and deauthorize Socio from there.")
		       .setCancelable(false)
		       .setTitle("Deauthorized Socio")
		       .setIcon(0)
		       .setNeutralButton("Close", onClickDismiss);
			dialog = builder.create();
			break;
		case NO_NICKNAME_DIALOG:
			builder.setMessage("You must enter a nickname to identify yourself to other users.")
		       .setCancelable(false)
		       .setTitle("No Nickname")
		       .setIcon(0)
		       .setNeutralButton("Close", onClickDismiss);
			dialog = builder.create();
			break;
		case NICKNAME_TOO_LONG:
			builder.setMessage("Your nickname can be at most 8 characters long.")
		       .setCancelable(false)
		       .setTitle("Nickname Too Long")
		       .setIcon(0)
		       .setNeutralButton("Close", onClickDismiss);
			dialog = builder.create();
			break;
		case SETTINGS_PROGRESS_DIALOG:
			progressDialog.setMessage("Getting your settings. Please wait...");
			progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
				public void onCancel(DialogInterface dialog) {
					backendThread.cancelMessageForMode(BackendThread.GET_SETTINGS);
					Toast.makeText(getApplicationContext(), "Settings retrieval canceled", Toast.LENGTH_LONG).show();
				}
			});
			dialog = progressDialog;
			break;
		}

		return dialog;
	}
	
	@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.settings_menu, menu);
        return true;
    }
    
	@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle item selection
        switch (item.getItemId()) {
        case R.id.TodayMenuItem:
        	Intent socioLauncher = new Intent(this, SocioLauncher.class)
        	.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        	startActivity(socioLauncher);
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }
	
	@Override
	protected void onPause() {
		super.onPause();
		messageToBeResumed = backendThread.pauseCurrentMessage();
	}

	@Override
	protected void onPrepareDialog(int id, Dialog dialog) {
		super.onPrepareDialog(id, dialog);
		if(dialog instanceof ProgressDialog)
			createdProgressDialogs.add((ProgressDialog)dialog);
		
	}

	@Override
	protected void onResume() {
		super.onResume();
		if(messageToBeResumed != null) {
			messageToBeResumed.setTarget(backendThreadsHandler);
			messageToBeResumed.sendToTarget();
		}
		
		// handle Twitter callback if there is one
		Intent thisIntent = getIntent();
		Uri uri = thisIntent.getData();
		thisIntent.setData(null);
		if (uri != null && uri.toString().startsWith(TWITTER_CALLBACK_URL)) {  
		    String verifier = uri.getQueryParameter("oauth_verifier");
		    try {
		    	twitterProvider.retrieveAccessToken(twitterConsumer, verifier);
		    	String accessToken = twitterConsumer.getToken() +"|"+ twitterConsumer.getTokenSecret();
		    	submitTwitterAccessToken(accessToken);
		    } catch (Exception e) {}
		} else if (uri != null && uri.toString().startsWith(FOURSQUARE_CALLBACK_URL)) {  
			submitFoursquareAccessToken(uri.getQueryParameter("code"));
		}
	}
	
	/**
	 * Submit new nickname for user
	 */
	private void submitNickname() {
		if(!isValidNickname())
			return;
		
		softKeyboard.hideSoftInputFromWindow(saveButton.getWindowToken(), 0);
		
		ClientUser user = new ClientUser();
		user.setAndroidUDID(telephonyManager.getDeviceId());
		user.setNickname(nickname.getText().toString());
		
		paramsForMessage.put("clientUser", user);
		
		// send message when current location is received
		final Message message = backendThreadsHandler.obtainMessage(BackendThread.UPDATE_SETTINGS);
		message.obj = paramsForMessage;
		message.sendToTarget();
	}
	
	/**
	 * Submit new Facebook ID for user
	 * 
	 * @param accessToken Token to use to get Facebook ID
	 */
	private void submitFacebookID(String accessToken) {
		showDialog(AUTHORIZATION_PROGRESS_DIALOG);
		
		ClientUser user = new ClientUser();
		user.setAndroidUDID(telephonyManager.getDeviceId());
		
		paramsForMessage.put("clientUser", user);
		paramsForMessage.put("accessToken", accessToken);
		
		// send message when current location is received
		final Message message = backendThreadsHandler.obtainMessage(BackendThread.UPDATE_FACEBOOKID);
		message.obj = paramsForMessage;
		message.sendToTarget();
	}
	
	/**
	 * Submit new Foursquare access token for user
	 * 
	 * @param code Code to use to get Foursquare access token
	 */
	private void submitFoursquareAccessToken(String code) {
		showDialog(AUTHORIZATION_PROGRESS_DIALOG);
		
		ClientUser user = new ClientUser();
		user.setAndroidUDID(telephonyManager.getDeviceId());
		
		paramsForMessage.put("clientUser", user);
		paramsForMessage.put("code", code);
		
		// send message when current location is received
		final Message message = backendThreadsHandler.obtainMessage(BackendThread.UPDATE_FOURSQUAREACCESSTOKEN);
		message.obj = paramsForMessage;
		message.sendToTarget();
	}
	
	/**
	 * Submit new Twitter Access Token for user
	 * 
	 * @param accessToken Token to use to sign api calls for user
	 */
	private void submitTwitterAccessToken(String accessToken) {
		showDialog(AUTHORIZATION_PROGRESS_DIALOG);
		
		ClientUser user = new ClientUser();
		user.setAndroidUDID(telephonyManager.getDeviceId());
		user.setTwitterAccessToken(accessToken);
		
		paramsForMessage.put("clientUser", user);
		
		// send message when current location is received
		final Message message = backendThreadsHandler.obtainMessage(BackendThread.UPDATE_TWITTERACCESSTOKEN);
		message.obj = paramsForMessage;
		message.sendToTarget();
	}
	
	/**
	 * Handle toggling of Facebook checkbox
	 */
	private void toggleFacebook() {
		
		boolean facebookChecked = facebookCheckbox.isChecked();
		
		if(!facebookChecked)
			showDialog(CANCEL_FACEBOOK_DIALOG);
		else
			authorizeThroughFacebook();
	}
	
	/**
	 * Handle toggling of Foursquare checkbox
	 */
	private void toggleFoursquare() {
		
		boolean foursquareChecked = foursquareCheckbox.isChecked();
		
		if(!foursquareChecked)
			showDialog(CANCEL_FACEBOOK_DIALOG);
		else
			authorizeThroughFoursquare();
	}
	
	/**
	 * Handle toggling of Twitter checkbox
	 */
	private void toggleTwitter() {
		
		boolean twitterChecked = twitterCheckbox.isChecked();
		
		if(!twitterChecked)
			submitTwitterAccessToken("");
		else
			authorizeThroughTwitter();
	}
}
